<?php
// +-----------------------------------------------------------------------+
// |                                                                       |
// | WebFormsQA     (Question - Answer)                                    |
// | Copyright  (C) 2010                                                   |
// | Official site  www.scale-tech.ru                                      |
// | email          scale-tech@mail.ru                                     |
// | autor          Alexander Demachev                                     |
// |                                                                       |
// | This program is free software; you can redistribute it and/or modify  |
// | it under the terms of the GNU General Public License as published by  |
// | the Free Software Foundation; version 2 of the License.               |
// |                                                                       |
// | This program is distributed in the hope that it will be useful,       |
// | but WITHOUT ANY WARRANTY; without even the implied warranty of        |
// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         |
// | GNU General Public License for more details.                          |
// +-----------------------------------------------------------------------+

//  константы класса
define('ID_AS_KEY', '0');		// интерпретировать ID как ключ		
define('ID_AS_COUNT', '1');		// интерпретировать ID как счетчик
define('NAME_AS_KEY', '2');		// интерпретировать имя как ключ

/*! 
 * @page DatabasePage Исходный код класса Database 
 * @include database.class.php
 */

/*! @addtogroup ClassWebFormsQA Основные классы проекта
 *  Группа включает основные классы используемые в проекте WebFormsQA
 *  @{
 */


/**
 * @class Database
 * @brief Базовый класс предназначен для работы с любой базой данных MySql, 
 *        является оберткой для вызова функций для работы с mysql, такими как 
 *        mysql_connect(), mysql_select_db(), mysql_query \n
 * @ref DatabasePage	        
 */
class Database {

	// конфигурационные параметры базы данных
    /** @var string $host 
     * 	@brief имя хоста
     */
    var $host;
    /** @var string $user 
     * 	@brief имя пользователя
     */
    var $user;
    
    /** @var string $pass 
     * 	@brief пароль пользователя
     */
    var $pass;
    
    /** @var string $name 
     * 	@brief имя базы данных
     */
    var $name;


    /**
     * @brief Конструктор, отвечает за передачу классу конфигурационных данных базы MySql
     *
     * @param string $host - имя хоста
     * @param string $user - имя пользовател
     * @param string $pass - пароль
     * @param string $name - имя Базы
     */
    function DataBase($host, $user, $pass, $name)
    {
       $this->host = $host;
       $this->user = $user;
       $this->pass = $pass;
       $this->name = $name;
    }


    /**
     * @brief метод проверяет установку соединения с использованием имени хоста, пользователя
     *        и пароля, которые переданы конструктору класса
     * @return integer  метод возвращает код операции
     * -			   		0 - проверка пройдена
     * -			   		1 - соединение не установлено
     * -			   		2 - база данных не выбрана 				
     */
    function checkHostUserPassAndNameBase(&$error)
    {
		if( !mysql_connect($this->host, $this->user, $this->pass) ) {
			$error = $this->db_error();
			return 1;
		}
		if( !$this->db_select_db($this->name) ) {
			$error = $this->db_error();
			return 2;
		}
		
       	return 0;
    }
    
    /**
     * @brief метод создает новые записи в служебной базе "mysql", создает запись с именем базы и пользователя с полным доступом к ней,
     *        для вызова метода необходим пароль root к базе MySql, устанавливаются следующие привилегии:<br>
     *        Select_priv, Insert_priv, Update_priv, Delete_priv, Create_priv, Drop_priv, Grant_priv, References_priv, Index_priv, Alter_priv, Create_tmp_table_priv, Lock_tables_priv
     * @param string $address_host 	- адрес хоста             
     * @param string $rootpass 		- пароль пользователя root
     * @param string $name_db 		- имя пользователя
     * @param string $login	 		- логин пользователя
     * @param string $password 		- пароль пользователя 
     * @param bool   $create_user 	- признак создания пользователя с указанным именем и проверки его существования
     * @param string &$error 		- сообщения о ошибке (пока не локализовано)
     * @param bool $flagUtf8		- признак использования кодировки UTF-8, иначе cp1251
     *  
     * @return boolean - метод возвращает успех операции
     */
    static function createDatabase($address_host, $rootpass, $name_db, $login, $password, $create_user, &$error, $flagUtf8=true )
    {
		// соединение с локальной базой с привилегиями суперпользователя
	    if (!mysql_connect($address_host, "root", $rootpass)) {
	    	 $error = "неправильный пароль пользователя root";
	      	 return false;
	    }
	    
	    if ($login=="" || !preg_match('/^[a-z0-9_]+$/i',$login)) {
	    	 $error = "имя пользователя не задано или содержит неправильные символы";
	      	 return false;
	    }
	    
	    mysql_select_db("mysql");
	    
	    if( $create_user ) {
		    $r = mysql_query("select * from user where user='$login'");
	    	if (mysql_num_rows($r)) {
		    	 $error = "пользователь с указанным именем уже есть в базе";
		      	 return false;
	    	}
	    }
	    
	    if ($name_db=="" || !preg_match('/^[a-z0-9_]+$/i',$name_db)) {
	    	 $error = "имя базы не задано или содержит неправильные символы";
	      	 return false;
	    }
	    
		$str_set=""; 
		if( $flagUtf8 ) $str_set=" DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci";
		else $str_set=" DEFAULT CHARACTER SET cp1251 COLLATE cp1251_general_ci";


	    if (!mysql_query("CREATE DATABASE `$name_db` $str_set")) {
	    	 $error = "не создана база данных с указанным именем, так как она уже существует";
	      	 return false;
	    }

	    if( $create_user ) {
		    $sql = "
		      INSERT INTO user (Host,            User,     Password,              File_priv) 
		      VALUES           ('$address_host', '$login', PASSWORD('$password'), 'Y'      )
		    ";
		    if (!mysql_query($sql)) {
		    	 $error = "ошибка записи данных в таблицу user";
		      	 return false;
		    }
	    }
	    
	    $sql = "
	      INSERT INTO db (
	        Host, Db, User, 
	        Select_priv, Insert_priv, Update_priv, Delete_priv, Create_priv, Drop_priv, Grant_priv, References_priv, Index_priv, Alter_priv, Create_tmp_table_priv, Lock_tables_priv 
	      ) VALUES (
	        '$address_host', '$name_db', '$login',
	        'Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y'
	      )
	    ";

    	if (!mysql_query($sql)) {
	    	 $error = "ошибка записи данных по привилегиям указанного пользователя";
	      	 return false;
    	}	

    	mysql_query("FLUSH PRIVILEGES");
       	return true;
    }
    
    /**
     * @brief метод устанавливает соединение с существующей базой данных,
     *        обертка функции mysql_connect()
     * @param string $host - имя хоста
     * @param string $user - имя пользовател
     * @param string $pass - пароль пользовател
     */
    function db_connect($host,$user,$pass)
    {
       return mysql_connect($host,$user,$pass);
    }

    /**
     * @brief метод выбирает базу данных, обертка функции mysql_select_db()
     * @param string $name 	- имя базы данных
     */
    function db_select_db($name)
    {
       return mysql_select_db($name);
    }

    /**
     * @brief метод запрашивает информацию из базу данных, обертка функции mysql_query
     * @param string $s - строка в формате SQL запроса
     */
    function db_query($s)
    {
       return mysql_query($s);
    }

    /**
     * @brief метод возвращает следующую строку запроса, обертка функции mysql_fetch_row
     * @param string $q - результат SQL запроса
    */
    function db_fetch_row($q)
    {
       return mysql_fetch_row($q);
    }

    /** @brief метод возвращает ID, сгенерированный при последнем INSERT-запросе,
     *         обертка функции mysql_insert_id()
     */
    function db_insert_id()
    {
       return mysql_insert_id();
    }

    /** @brief метод возвращает количество рядов результата запроса SELECT 
     * 		   обертка функции mysql_num_rows()	
     */
    function db_num_rows($q)
    {
        return mysql_num_rows($q);
    }

    /** @brief метод возвращает количество рядов обработанных функцями INSERT, UPDATE, DELETE 
     *		   обертка функции mysql_affected_rows()
     */
    function db_affected_rows()
    {
        return mysql_affected_rows();
    }


    /** @brief вывод сообщения о последней ошибке, обертка функции mysql_error() */
    function db_error()
    {
       return mysql_error();
    }

    /**
     * @brief Метод удаляет из строки все некорректные символы
     *        которые нельзя использовать в строке поиска, особенно при MySql запросе с "LIKE",
     *        удаляются "%","_", слеши, а все символы "'" экранируется обратным слешем в целях безопасности  
     * @param string $s - строка для поиска
     * @return string   - метод возвращает измененную строку
     */
    function validate_search_string($s)
    {
       //exclude special SQL symbols
       $s = str_replace("%","",$s);
       $s = str_replace("_","",$s);
       //",',\
       $s = stripslashes($s);
       $s = str_replace("'","\'",$s);
       return $s;
    } // end function validate_search_string
    

    /**
     * @brief Инициализация, метод отвечает за создание соединения
     *        с существующей БАЗОЙ ДАННЫХ, используя конфигурационные настройки
     *        переданные конструктору
     * @return          - метод возвращает успех операции
     */
    function Init($flagUtf8=true)
    {
        $this->db_connect($this->host, $this->user, $this->pass ) or die($this->db_error());
        $this->db_select_db($this->name) or die($this->db_error());

		if( $flagUtf8 ) $this->db_query( "SET NAMES 'utf8'" );
		else $this->db_query( "SET NAMES 'cp1251'" );

        return true;
    }

    /**
     * @brief Метод добавляет уникальное имя "$name" в поле "$name_field"
     *        таблицы "$name_table", если данное имя уже есть в таблице, 
     *        то возвращается идентификатор данного имени "ID" <br>
     *        
     * ПРИМЕЧАНИЕ: метод удаляет из строки $name все специальные символы
     *
     * @param string $name_table    - название таблицы
     * @param string $name_field    - название поля таблицы
     * @param string $name          - имя для добавлени
     * @param integer &$id          - идентификатор добавленной или найденной записи
     * @return boolean              - успех операции
     */
    function addNameInTable($name_table, $name_field, $name, &$id)
    {
        $flag_result = false;
        $id = 0;
        $name = $this->validate_search_string($name);

        $q = $this->db_query("SELECT ID FROM ".$name_table." WHERE ".$name_field."='$name' LIMIT 1 ") or die($this->db_error());
        $row = $this->db_fetch_row($q);
        if ($row) {
            $id = $row[0];
        } else {
            $this->db_query("INSERT INTO ".$name_table."( ".$name_field." ) VALUES ( '$name' )") or die($this->db_error());
            $id = $this->db_insert_id();
            $flag_result = true;
        }

        return $flag_result;
    } // end function addNameInTable

    /**
     * @brief Метод ищет уникальное имя "$name" в поле "$name_field"
     *        таблицы "$name_table", если данное имя найдено в таблице, 
     *        то возвращается идентификатор данного имени "ID", иначе 0
     *
     * примечание: метод удаляет из строки $name все специальные символы
     *
     * @param string $name_table    - название таблицы
     * @param string $name_field    - название поля таблицы
     * @param string $name          - имя для добавлени
     * @return integer              - идентификатор
     */
    function searchNameInTable($name_table, $name_field, $name)
    {
        $id = 0;
        $name = $this->validate_search_string($name);
        $q = $this->db_query("SELECT ID FROM ".$name_table." WHERE ".$name_field."='$name' LIMIT 1 ") or die($this->db_error());
      
        $row = $this->db_fetch_row($q);
        if ($row) {
        	$id = $row[0];
        }

        return $id;
    } // end function searchNameInTable

    /**
     * @brief Метод возвращает строку хранящиеся в поле $name_field
     * 		  таблицы $name_table, запись ищется по идентификатору $id
     *
     * @param string $name_table    - название таблицы
     * @param string $name_field    - название поля таблицы
     * @param integer $id           - идентификатор искомой записи
     * @return string               - найденная или пустая строка
     */
    function getNameFromTable($name_table, $name_field, $id)
    {
        $str_result = "";
        $q = $this->db_query("SELECT ".$name_field." FROM ".$name_table." WHERE ID='$id' LIMIT 1 ") or die($this->db_error());

        if ($row = $this->db_fetch_row($q)) {
            $str_result = $row[0];
        }

        return $str_result;
    } // getNameFromTable

    /**
     * @brief Метод возвращает все данные одной записи таблицы $name_table,
     * 		  запись ищется по идентификатору $id (название в таблице - "ID")
     *
     * @param string $name_table    - название таблицы
     * @param integer $id           - идентификатор искомой записи
     * @param array &$data          - возвращаемые данные, числовые индексы - где значения
     *                                полей следуют в порядке создания таблицы
     * @return bool                 - успех операции
     */
    function getDataFromTable($name_table, $id, &$data)
    {
        $flag_result = false;
        $q = $this->db_query("SELECT * FROM ".$name_table." WHERE ID='$id' LIMIT 1 ") or die($this->db_error());

        if ($row = $this->db_fetch_row($q)) {
            $flag_result    = true;
            foreach($row as $mean) {
                $data[] = $mean;
            }
        }

        return $flag_result;
    } // getDataFromTable

    /**
     * @brief Метод обновляет данные одной записи таблицы $name_table,
     * 		  обновляется запись с идентификатором $id (название в таблице - ID)
     *
     * @param string $name_table    - название таблицы
     * @param integer $id           - идентификатор записи
     * @param array &$data          - данные для записи где:
     * -                              ключи - названия полей таблицы
     * -                              значения - данные для обновления<br>
     * ПРИМЕЧАНИЕ:  поля должны соответствовать полям таблицы $name_table
     * @return bool                 - успех операции
     */
    function setDataInTable($name_table, $id, $data)
    {
        $flag_result = false;

        $size = count($data) - 1;
        $i = 0;
		$str = "";
        // формируем поля и значени
        foreach ($data as $field => $mean ) {
            $str .= $field."='".$mean."'";
            if ($i<$size) {
                $str .= ", ";
            }
            $i++;
        }

        $q = $this->db_query("UPDATE ".$name_table." SET ".$str." WHERE ID='$id' LIMIT 1 ") or die($this->db_error());
        if ($q) $flag_result = true;

        return $flag_result;
    } // setDataInTable

    /**
     * @brief Метод удаляет запись из таблицы "$name_table"
     *
     * @param string $name_table    - название таблицы
     * @param integer $id           - идентификатор записи("ID")
     * @return boolean              - успех операции
     */
    function delEntryFromTable($name_table, $id)
    {
        $flag_result = false;

        $q = $this->db_query("DELETE FROM ".$name_table." WHERE ID='$id' LIMIT 1 ") or die($this->db_error());
        if ($q) $flag_result = true;
        
        return $flag_result;
    } 

    /**
     * @brief Метод формирует строку запроса для условия WHERE ввиде простых операций AND или OR, например:
     * @code     
     *        ID='1' OR ID='2' OR ID='3'
     *        ID='1' AND ID='2' AND ID='3'
     * @endcode       
     *
     * @param string $name_field    - название поля таблицы (идентификатора) например 'ID'
     * @param array &$idx           - массив идентификаторов
     * @param string &$out          - строка с результатом запроса
     * @param bool  $oper_and       - тип операции true - "AND" false - "OR"
     * @return integer              - количество элементов в строке запроса
     */
    function getRequest($name_field, &$idx, &$out, $oper_and = true)
    {
        $size       = sizeof($idx);
        if ($size) {
            // начало строки запроса
            $out    = $name_field."='$idx[0]'";

            if ($oper_and) {
                for ($i=1; $i < $size; $i++) $out .= " AND ".$name_field."='$idx[$i]'";
            } else {
                for ($i=1; $i < $size; $i++) $out .= " OR ".$name_field."='$idx[$i]'";
            }
        }

        return $size;
    } // end function searchNameInTable



} // end class Database

/*! @} */

?>
